/********************************************
 * 功能说明: 
 * 模块名称: 
 * 系统名称: 
 * 软件版权: 
 * 系统版本: 1.0.0
 * 开发人员: zhangfb
 * 开发时间: 2019/5/29 21:41
 * 审核人员: 
 * 相关文档: 
 * 修改记录: 修改日期 修改人员 修改说明
 *********************************************/
package com.hyacinth.aurora.core.context;

import com.hyacinth.aurora.AuroraServer;
import com.hyacinth.aurora.constant.Constant;
import com.hyacinth.aurora.constant.ServerChannel;
import com.hyacinth.aurora.constant.StatusEnum;
import com.hyacinth.aurora.core.AuroraProperties;
import com.hyacinth.aurora.core.ClassHelper;
import com.hyacinth.aurora.core.bean.BeanManager;
import com.hyacinth.aurora.core.configuration.AbstractConfiguration;
import com.hyacinth.aurora.core.configuration.ConfigurationHolder;
import com.hyacinth.aurora.core.exception.SingleClassInstanceException;
import com.hyacinth.aurora.core.plugin.PluginHelper;
import com.hyacinth.aurora.core.exception.AuroraException;
import com.hyacinth.aurora.mvc.ActionHelper;
import com.hyacinth.aurora.orm.ORMOptionHelper;
import com.hyacinth.aurora.utils.Assert;
import com.hyacinth.aurora.utils.CollectionUtil;
import com.hyacinth.aurora.utils.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;

import static com.hyacinth.aurora.constant.Constant.SystemProperties.APPLICATION_THREAD_MAIN_NAME;
import static com.hyacinth.aurora.constant.Constant.SystemProperties.LOGO;

/**
 * 全局配置加载器
 *
 * @author zhangfb
 * @version 1.0.0.1
 * @since JDK 1.8
 */
public final class AuroraContextHolder {

    private static final Logger LOGGER = LoggerFactory.getLogger(AuroraContextHolder.class);

    /**
     * 初始化操作
     * @param clazz 启动类
     */
    public static void init(Class<?> clazz) {
        try {
            // 打印logo
            logo();
            // 配置初始化
            initConfiguration(clazz);
            // 初始化对象关系映射ORM
            initORM();
            // 初始化Action
            initAction();
            // 初始化bean工厂
            initBeanFactory();
            // 初始化插件
            intPlugins();
        } catch (Throwable e) {
            throw new AuroraException("初始化操作出错", e);
        }
    }

    /**
     * 初始化操作
     *
     * @param clazz 启动类
     */
    private static void initConfiguration(Class<?> clazz) {
        // 初始化应用核心配置
        initApplicationConfiguration(clazz);
        // 加载外部配置
        initExternalConfiguration();
    }

    /**
     * 1、打印logo
     * 2、设置当前线程名
     * 3、设置当前应用初始化时间
     */
    private static void logo() {
        System.out.println(LOGO);
        Thread.currentThread().setName(APPLICATION_THREAD_MAIN_NAME);
        ThreadLocalHolder.setLocalTime(System.currentTimeMillis());
    }

    /**
     * 设置应用配置
     * @param clazz 启动类
     */
    private static void initApplicationConfiguration(Class<?> clazz) {
        Assert.notNull(clazz, "main class is null!!!");
        if (clazz.getPackage() == null) {
            throw new AuroraException(StatusEnum.NULL_PACKAGE, "[" + clazz.getName() + ".java]");
        }
        // 获取服务上下文路径
        String serverContextPath = AuroraProperties.getProperty(Constant.SERVER_CONTEXT_PATH);
        if (serverContextPath == null) {
            serverContextPath = clazz.getSimpleName().toLowerCase();
            LOGGER.info("aurora.properties no [server.context.path] prop exists, default context path is [{}]", serverContextPath);
        }

        // 获取启动端口
        int port = 8080;
        try {
            port = Integer.parseInt(AuroraProperties.getProperty(Constant.SERVER_PORT));
        } catch (Exception e) {
            LOGGER.info("aurora.properties error [server.port] prop exists, default server port is [8080]");
        }

        // 如果未配置，默认是Netty
        ServerChannel serverChannel = ServerChannel.Netty;
        String serverChannelName = AuroraProperties.getProperty(Constant.SERVER_CHANNEL);
        if (serverChannelName != null) {
            serverChannel = ServerChannel.getByName(serverChannelName);
        }

        // 设置应用配置类
        AppConfig appConfig = AppConfig.INSTANCE;
        appConfig.setPort(port);
        appConfig.setServerChannel(serverChannel);
        appConfig.setServerContextPath(serverContextPath);
        appConfig.setRootPackageName(clazz.getPackage().getName());
    }

    /**
     * 加载外部配置，均继承AbstractConfiguration类
     */
    private static void initExternalConfiguration() {
        // 加载所有的配置类
        List<Class<?>> configurations = ClassHelper.getClassListBySuper(AbstractConfiguration.class);
        if (CollectionUtil.isNotEmpty(configurations)) {
            for (Class<?> aClass : configurations) {
                try {
                    AbstractConfiguration conf = (AbstractConfiguration) aClass.newInstance();
                    String propertiesName = conf.getPropertiesName();
                    if (StringUtil.isBlank(propertiesName)) {
                        LOGGER.warn("配置类未设置配置文件名称，配置类：[{}]", aClass.getName());
                        continue;
                    }
                    InputStream stream;
                    // 从启动参数中获取配置文件名称
                    String sysProperty = System.getProperty(propertiesName);
                    if (sysProperty != null) {
                        stream = new FileInputStream(new File(sysProperty));
                    } else {
                        // 使用启动类的加载器加载属性文件流
                        stream = AuroraServer.class.getClassLoader().getResourceAsStream(propertiesName);
                    }

                    Properties properties = new Properties();
                    properties.load(stream);
                    conf.setProperties(properties);

                    // 设置文件全局缓存
                    ConfigurationHolder.addConfiguration(aClass.getName(), conf);
                } catch (Exception e) {
                    LOGGER.error("load configuration error, configuration class [{}]", aClass.getName());
                }
            }
        }
    }

    private static void initORM() throws SingleClassInstanceException {
        ORMOptionHelper.getInstance(ORMOptionHelper.class).init();
    }

    private static void initAction() throws SingleClassInstanceException {
        ActionHelper.getInstance(ActionHelper.class).init();
    }

    private static void initBeanFactory() throws SingleClassInstanceException {
        BeanManager.getInstance(BeanManager.class).init();
    }

    private static void intPlugins() throws SingleClassInstanceException {
        PluginHelper.getInstance(PluginHelper.class).init();
    }

}
